class MainLogic {
	public static UP = 0;
	public static DOWN = 1;
	public static LEFT = 2;
	public static RIGHT = 3;

	public constructor() {
	}

	private static set_move_table(move_table:Array<number>, from:number, to:number, comb_table:Array<number>)
	{
		for (let i = 0; i < 4; i++)
		{
			if (move_table[i] == from)
			{
				move_table[i] = to;
			}
			if (comb_table[from] == 1)
			{
				comb_table[from] = 0;
				comb_table[to] = 1;
			}
		}
	}

	//表格move_line中有4个数字，从下标为3的项向下标为0的项合并
	private static moveLine(move_line:Array<number>, move_table:Array<number>, comb_table:Array<number>):boolean
	{
		let ismove = false;
		//先把相邻一样的合并
		for (let i = 0; i < 4-1; i++)
		{
			if (move_line[i] != 0)
			{
				let k = i + 1;
				while (k < 4)
				{
					if (move_line[k] != 0)
					{
						break;
					}
					k++;
				}
				if (move_line[i] == move_line[k])
				{
					move_line[i] = 2 * move_line[i];
					move_line[k] = 0;
					ismove = true;
					comb_table[i] = 1;
					MainLogic.set_move_table(move_table, k, i, comb_table);
				}
			}
		}
		//再把0剔除
		for (let i = 0; i < 4-1; i++)
		{
			let count = 0;
			while (move_line[i] == 0)
			{
				count++;
				if (count == 4-i)
				{
					break;
				}
				for (let k = i; k < 4-1; k++)
				{
					move_line[k] = move_line[k+1];
					if (move_line[k+1] != 0)
					{
						ismove = true;
					}
					move_line[k+1] = 0;
					MainLogic.set_move_table(move_table, k + 1, k, comb_table);
				}
			}
		}

		return ismove;
	}

	private static up(gameData:Array<number[]>, move_table:Array<number[]>, comb_table:Array<number[]>):boolean
	{
		let ismove = [false, false, false, false];
		for (let i = 0; i < 4; i++)
		{
			let move_line = new Array<number>();
			move_table[i] = new Array<number>();
			comb_table[i] = new Array<number>();
			for (let j = 0; j < 4; j++)
			{
				move_line[j] = gameData[j][i];
				move_table[i][j] = j;
			}
			ismove[i] = MainLogic.moveLine(move_line, move_table[i], comb_table[i]);
			for (let j = 0; j < 4; j++)
			{
				gameData[j][i] = move_line[j];
			}
		}
		let ret = false;
		for (let i = 0; i < 4; i++)
		{
			if (ismove[i] == true)
			{
				ret = true;
				break;
			}
		}
		return ret;
	}

	private static down(gameData:Array<number[]>, move_table:Array<number[]>, comb_table:Array<number[]>):boolean
	{
		let ismove = [false, false, false, false];
		for (let i = 0; i < 4; i++)
		{
			let move_line = new Array<number>();
			move_table[i] = new Array<number>();
			comb_table[i] = new Array<number>();
			for (let j = 3; j >= 0; j--)
			{
				move_line[3 - j] = gameData[j][i];
				move_table[i][j] = j;
			}
			ismove[i] = MainLogic.moveLine(move_line, move_table[i], comb_table[i]);
			for (let j = 3; j >= 0; j--)
			{
				gameData[j][i] = move_line[3 - j];
			}
		}
		let ret = false;
		for (let i = 0; i < 4; i++)
		{
			if (ismove[i] == true)
			{
				ret = true;
				break;
			}
		}
		return ret;
	}

	private static left(gameData:Array<number[]>, move_table:Array<number[]>, comb_table:Array<number[]>):boolean
	{
		let ismove = [false, false, false, false];
		for (let i = 0; i < 4; i++)
		{
			move_table[i] = new Array<number>();
			comb_table[i] = new Array<number>();
			for (let j = 0; j < 4; j++)
			{
				move_table[i][j] = j;
			}
			ismove[i] = MainLogic.moveLine(gameData[i], move_table[i], comb_table[i]);
		}
		let ret = false;
		for (let i = 0; i < 4; i++)
		{
			if (ismove[i] == true)
			{
				ret = true;
				break;
			}
		}
		return ret;
	}

	private static right(gameData:Array<number[]>, move_table:Array<number[]>, comb_table:Array<number[]>):boolean
	{
		let ismove = [false, false, false, false];
		for (let i = 0; i < 4; i++)
		{
			let move_line = new Array<number>();
			move_table[i] = new Array<number>();
			comb_table[i] = new Array<number>();
			for (let j = 3; j >= 0; j--)
			{
				move_line[3 - j] = gameData[i][j];
				move_table[i][j] = j;
			}
			ismove[i] = MainLogic.moveLine(move_line, move_table[i], comb_table[i]);
			for (let j = 3; j >= 0; j--)
			{
				gameData[i][j] = move_line[3 - j];
			}
		}
		let ret = false;
		for (let i = 0; i < 4; i++)
		{
			if (ismove[i] == true)
			{
				ret = true;
				break;
			}
		}
		return ret;
	}

	public static move(gameData:Array<number[]>, position:number, move_table:Array<number[]>, comb_table:Array<number[]>, ret_array:Array<number>):boolean
	{
		let ismove = false;

		switch(position)
		{
		case MainLogic.UP:
			ismove = MainLogic.up(gameData, move_table, comb_table);
			break;
		case MainLogic.DOWN:
			ismove = MainLogic.down(gameData, move_table, comb_table);
			break;
		case MainLogic.LEFT:
			ismove = MainLogic.left(gameData, move_table, comb_table);
			break;
		case MainLogic.RIGHT:
			ismove = MainLogic.right(gameData, move_table, comb_table);
			break;
		default:
			return ismove;
		}
		if (ismove == false)
		{
			return ismove;
		}

		let zero_num = 0;
		for (let i = 0; i < 4; i++)
		{
			for (let j = 0; j < 4; j++)
			{
				if (gameData[i][j] == 2048)
				{
					ret_array[1] = 1;
					//return ismove;
				}
				if (gameData[i][j] == 0)
				{
					zero_num++;
				}
			}
		}
		if (zero_num == 0)
		{
			return ismove;
		}

		let add_num_two = (Math.floor(Math.random()*100000000) % (zero_num - 1 + 1) + 1);
		for (let i = 0; i < 4; i++)
		{
			for (let j = 0; j < 4; j++)
			{
				if (gameData[i][j] == 0)
				{
					add_num_two--;
					if (add_num_two == 0)
					{
						let num = 2, random = MainLogic.getRandom(1, 100);
						if (random > 0 && random < 80)
							num = 2;
						else
							num = 4;
						gameData[i][j] = num;
						break;
					}
				}
			}
			if (add_num_two == 0)
			{
				break;
			}
		}

		if (zero_num == 1)
		{
			for (let i = 0; i < 4; i++)
			{
				for (let j = 0; j < 3; j++)
				{
					if (gameData[i][j] == gameData[i][j+1])
					{
						return ismove;
					}
					if (gameData[j][i] == gameData[j+1][i])
					{
						return ismove;
					}
				}
			}
			ret_array[0] = 1;
		}

		return ismove;
	}

	public static getRandom(from:number, to:number):number
	{
		return (Math.floor(Math.random()*100000000) % (to - from + 1) + from);
	}

	public static getStorageString(gameData:Array<number[]>):string
	{
		let str:string = "";

		for (let i = 0; i< 4; i++)
		{
			for (let j = 0; j < 4; j++)
			{
				str += gameData[i][j];
				if (i != 3 || j != 3)
					str += ",";
			}
		}
		
		return str;
	}

	public static getStorageData(str:string):Array<number[]>
	{
		if (!str)
			str = "";
		
		let data:Array<number[]> = new Array<number[]>();

		let strs:string[] = str.split(",");
		for (let i = 0; i < 4; i++)
		{
			data[i] = new Array<number>();
			for (let j = 0; j < 4; j++)
			{
				if (strs[i * 4 + j])
					data[i][j] = parseInt(strs[i * 4 + j]);
				else
					data[i][j] = 0;
			}
		}

		return data;
	}

	public static getLastStorageString(lastData:Array<Array<number[]>>, len:number):string
	{
		let str:string = "";

		for (let k = 0; k < len; k++)
		{
			for (let i = 0; i< 4; i++)
			{
				for (let j = 0; j < 4; j++)
				{
					str += lastData[k][i][j];
					if (i != 3 || j != 3)
						str += ",";
				}
			}
			if (k != len - 1)
				str += "|";
		}
		
		return str;
	}

	public static getLastStorageData(str:string, len:number):Array<Array<number[]>>
	{
		if (!str || len <= 0)
			return null;
		
		let data:Array<Array<number[]>> = new Array<Array<number[]>>();

		let datastrs:string[] = str.split("|");
		for (let k = 0; k < len && k < datastrs.length; k++)
		{
			let strs:string[] = datastrs[k].split(",");

			data[k] = new Array<number[]>();
			for (let i = 0; i < 4; i++)
			{
				data[k][i] = new Array<number>();
				for (let j = 0; j < 4; j++)
				{
					data[k][i][j] = parseInt(strs[i * 4 + j]);
				}
			}
		}

		return data;
	}
}